package org.medusa.sample;

import java.io.IOException;
import java.io.OutputStream;

import org.medusa.packet.blob.BinaryPacket;

//1.分离数据包头，将packet变为抽象类
public class Packet extends BinaryPacket {

	public static final int PACKET_HEAD_LEN = 34;

	private byte m; // byte //1:+ |2:- |3:* |4/
	private short a; // A, B int16
	private short b;
	private short c;
	private int len;
	private short des;
	private int uuid;
	private boolean is_space_msg;
	private int except_uuid;
	private int doer_or_result;
	private long timestamp;

	// tool
	private byte pack_type = 0;
	public static byte PACKET_TYPE_REQUEST = 1;
	public static byte PACKET_TYPE_RESPONSE = 2;
	public static byte PACKET_TYPE_NOTIFY = 3;

	public static Packet NewRequest(long des, int bodylen) {
		Packet pack = new Packet(des, bodylen);
		pack.pack_type = Packet.PACKET_TYPE_REQUEST;
		return pack;
	}

	public static Packet NewRequest(long des) {
		Packet pack = new Packet(des);
		pack.pack_type = Packet.PACKET_TYPE_REQUEST;
		return pack;
	}
	
	public static Packet NewResponse(int des, /*long timestamp, */int result_err) {
		Packet pack = new Packet(des);
		pack.pack_type = Packet.PACKET_TYPE_RESPONSE;
		//pack.timestamp = timestamp;
		pack.SetResponseResult(result_err);
		return pack;
	}

	public static Packet NewResponse(Packet request, int result_err) {
		Packet pack = new Packet(request.Des());
		pack.pack_type = Packet.PACKET_TYPE_RESPONSE;
		pack.timestamp = request.timestamp;
		pack.SetResponseResult(result_err);
		return pack;
	}

	public static Packet NewResponse(Packet request, int result_err, int bodylen) {
		Packet pack = new Packet(request.Des(), bodylen);
		pack.pack_type = Packet.PACKET_TYPE_RESPONSE;
		pack.timestamp = request.timestamp;
		pack.SetResponseResult(result_err);
		return pack;
	}

	public static Packet NewNotify(long des, int doer_uuid) {
		Packet pack = new Packet(des);
		pack.pack_type = Packet.PACKET_TYPE_NOTIFY;
		pack.set_notify_uuid(doer_uuid);
		return pack;
	}

	public static Packet RequestToNotify(Packet pack, int des, int doer_uuid) {
		pack.des = (short) des;
		pack.pack_type = Packet.PACKET_TYPE_NOTIFY;
		pack.set_notify_uuid(doer_uuid);
		return pack;
	}

	public void ToNotifyPacket(long des, int doer_uuid) {
		this.des = (short) des;
		this.pack_type = Packet.PACKET_TYPE_NOTIFY;
		this.set_notify_uuid(doer_uuid);
	}

	public boolean IsRequest() {
		return this.pack_type == Packet.PACKET_TYPE_REQUEST;
	}

	public boolean IsResponse() {
		return this.pack_type == Packet.PACKET_TYPE_RESPONSE;
	}

	public boolean IsNotify() {
		return this.pack_type == Packet.PACKET_TYPE_NOTIFY;
	}

	private Packet(long des) {
		this(des, 1024 * 2); // 2KB
	}

	private Packet(long des, int bodylen) {
		super(Packet.PACKET_HEAD_LEN, bodylen);
		// this.m = 4;
		// this.a = 7;
		// this.b = 3;
		// this.c = 2;
		this.gen_mabc();
		this.len = Packet.PACKET_HEAD_LEN;
		this.des = (short) des;
		this.uuid = 0;
		this.is_space_msg = false;
		this.except_uuid = 0;
		this.doer_or_result = 0;
		this.timestamp = 0;
		/*
		 * this.head.WriteByte(this.m); this.head.WriteInt16(this.a);
		 * this.head.WriteInt16(this.b); this.head.WriteInt16(this.c);
		 * this.head.WriteInt32(this.len); this.head.WriteInt16(this.des);
		 * this.head.WriteInt32(this.uuid); if (this.is_space_msg) {
		 * this.head.WriteByte((byte) 1); } else { this.head.WriteByte((byte)
		 * 0); } this.head.WriteInt32(this.except_uuid);
		 * this.head.WriteInt32(this.doer_or_result);
		 * this.head.WriteInt64(this.timestamp);
		 */
	}

	// err-
	public Packet(byte m, short a, short b, short c, int len, short des,
			int uuid, boolean is_space_msg, int except_uuid,
			int notify_or_result, long timestamp) {
		super(Packet.PACKET_HEAD_LEN, len);
		this.pack_type = (des % 2 != 0) ? Packet.PACKET_TYPE_RESPONSE
				: Packet.PACKET_TYPE_NOTIFY;
		this.m = m;
		this.a = a;
		this.b = b;
		this.c = c;
		this.len = len;
		this.des = des;
		this.uuid = uuid;
		this.is_space_msg = is_space_msg;
		this.except_uuid = except_uuid;
		this.doer_or_result = notify_or_result;
		this.timestamp = timestamp;
		/*
		 * this.head.WriteByte(this.m); this.head.WriteInt16(this.a);
		 * this.head.WriteInt16(this.b); this.head.WriteInt16(this.c);
		 * this.head.WriteInt32(this.len); this.head.WriteInt16(this.des);
		 * this.head.WriteInt32(this.uuid); if (this.is_space_msg) {
		 * this.head.WriteByte((byte) 1); } else { this.head.WriteByte((byte)
		 * 0); } this.head.WriteInt32(this.except_uuid);
		 * this.head.WriteInt32(this.doer_or_result);
		 * this.head.WriteInt64(this.timestamp);
		 */
	}

	public int PacketSize() {
		return this.len;
	}

	public int DataSize() {
		return this.body.DataSize() + Packet.PACKET_HEAD_LEN;
	}

	private void gen_mabc() {
		this.m = (byte) (1 + Math.rint(3));
		this.a = (short) (1 + Math.rint(127));
		this.b = (short) (1 + Math.rint(127));

		if (this.m == 1)
			this.c = (short) (this.a + this.b);
		else if (this.m == 2)
			this.c = (short) (this.a - this.b);
		else if (this.m == 3)
			this.c = (short) (this.a * this.b);
		else if (this.m == 4)
			this.c = (short) (this.a / this.b);
	}

	public long Des() {
		return this.des;
	}

	public long TimeStamp() {
		return this.timestamp;
	}

	public void SetTimeStamp() {
		this.timestamp = System.currentTimeMillis();
	}

	public boolean IsSpaceMsg() {
		return this.is_space_msg;
	}

	public void SetSpaceMode(int space_uuid, int except_uuid) {
		this.uuid = space_uuid;
		this.is_space_msg = true;
		this.except_uuid = except_uuid;
	}

	public void SetSpaceMode(int space_uuid) {
		this.uuid = space_uuid;
		this.is_space_msg = true;
		this.except_uuid = 0;
	}

	public void SetSessionMode(int session_uuid) {
		this.uuid = session_uuid;
		this.is_space_msg = false;
		this.except_uuid = 0;
	}

	public int BroadcastExceptUUID() {
		return this.except_uuid;
	}

	public int RequestUUID() {
		return this.uuid;
	}

	public int ResponseResult() {
		return this.doer_or_result;
	}

	public void SetResponseResult(int err) {
		this.doer_or_result = err;
	}

	public int NotifyUUID() {
		return this.doer_or_result;
	}

	private void set_notify_uuid(int obj_uuid) {
		this.doer_or_result = obj_uuid;
	}

	@Override
	public Error WriteHead(OutputStream output) {
		Error err = null;
		this.head.Clear();

		this.head.WriteInt8(this.m);//1
		this.head.WriteInt16LE(this.a);//3
		this.head.WriteInt16LE(this.b);//5
		this.head.WriteInt16LE(this.c);//7
		this.head.WriteInt32LE(this.DataSize());//11
		this.head.WriteInt16LE(this.des);//13
		this.head.WriteInt32LE(this.uuid);//17
		this.head.WriteInt8(this.is_space_msg ? (byte) 1 : (byte) 0);//18
		this.head.WriteInt32LE(this.except_uuid);//22
		this.head.WriteInt32LE(this.doer_or_result);//26
		this.head.WriteInt64LE(this.timestamp);//34

		try {
			output.write(this.head.GetBytes(), this.head.GetReadOffset(),
					this.head.GetWriteOffset());
		} catch (IOException e) {
			// e.printStackTrace();
			err = new Error(e.getMessage());
		}

		return err;
	}

	@Override
	public void Trace(String pre) {
		System.out.println("[" + pre + "]:\nM:" + this.m + "|A:" + this.a
				+ "|B:" + this.b + "|C:" + this.c + "|Len:" + this.len
				+ "|MSG:" + this.des + "|session:" + this.uuid + "|is_space:"
				+ this.is_space_msg + "|except:" + this.except_uuid
				+ "|notify_or_result:" + this.doer_or_result + "|timestamp:"
				+ this.timestamp);
	}

	@Override
	public boolean Check() {
		boolean ret = false;
		switch (this.m) {
		case 1:
			ret = this.c == (this.a + this.b);
			break;
		case 2:
			ret = this.c == (this.a - this.b);
			break;
		case 3:
			ret = this.c == (this.a * this.b);
			break;
		case 4:
			ret = this.c == (this.a / this.b);
			break;
		default:
			break;
		}

		return ret;
	}
	
}
